use anyhow::Result;
use fudata::backtest::back_test;
use fudata::backtest::trade_engine::TradeEngine;
use fudata::backtest::TradeRequest;
use fudata::db;
use fudata::db::history;
use fudata::model::history::FudaHistory;
use fudata::model::indicators::Indicator;
use fudata::model::kline_flat::KlineFlat;
use fudata::model::kline_flat::TrendLineResult;
use fudata::model::matrix::CorrelationMatrix;
use fudata::model::matrix::IndicatorComb;
use fudata::model::security::Security;
use fudata::model::table::Column;
use fudata::model::table::Table;
use fudata::model::trade_bot_with::TradeBot;
use fudata::model::trade_order::TradeOrder;
use fudata::model::trade_strategy::TradeStrategy;
use fudata::model::trade_strategy::TradeStrategyIndicator;
use fudata::model::trade_strategy::TradeStrategyMaLine;
use fudata::model::FudaRecord;
use rhai::AST;
use serde::Deserialize;
use serde::Serialize;
use specta::Type;

use crate::ipc::FudaHistoryFilter;
use crate::ipc::GetParams;
use crate::ipc::IndicatorCombinationFilter;
use crate::ipc::IndicatorFilter;
use crate::ipc::TradeBotFilter;
use crate::ipc::TradeOrderFilter;
use crate::ipc::TradeStrategyFilter;
use crate::ipc::{IpcResponse, KlineFilter, ListParams, UpdateParams};
use crate::kline::financial_dp::ChartJsDataWrapper;
use crate::kline::financial_dp::FinancialDataPoint;

// The `specta::Type` macro allows us to understand your types
// We implement `specta::Type` on primitive types for you.
// If you want to use a type from an external crate you may need to enable the feature on Specta.
#[derive(Serialize, Type, Debug)]
pub struct MyCustomReturnType {
    pub some_field: String,
}

#[derive(Deserialize, Type, Debug)]
pub struct MyCustomArgumentType {
    pub foo: String,
    pub bar: i32,
}

#[tauri::command]
#[specta::specta] // <-- This bit here
pub fn greet(name: String) -> String {
    format!("Hello {name}!")
}

#[tauri::command]
#[specta::specta] // <-- This bit here
pub fn greet2(args: MyCustomArgumentType) -> MyCustomReturnType {
    MyCustomReturnType {
        some_field: format!("Hello World for {:?} !", args),
    }
}

// 由于Security没用specta::specta注解，手工参照greet2修改commands.ts文件
#[tauri::command]
pub fn greet3(args: MyCustomArgumentType) -> Security {
    Security {
        name: Some(format!("foo={} bar={}", args.foo, args.bar)),
        ..Default::default()
    }
}

// 由于Security没用specta::specta注解，手工参照greet2修改commands.ts文件
#[tauri::command]
pub async fn list_securities(args: ListParams<KlineFilter>) -> IpcResponse<Vec<Security>> {
    println!("list_securities args={:?}", args);
    let mut pi: u16 = 0;
    let mut pn: u16 = 10;
    if let Some(page) = args.page {
        pi = page;
    }
    if let Some(size) = args.size {
        pn = size;
    }

    let rs: Result<Vec<Security>, anyhow::Error> = fudata::db::security::select(&None,None,None,None, pi, pn).await;

    // println!("(commands:list_securities) rs = {:?}", rs);

    rs.into()
}

/// 由于TradeStrategy用不了specta::specta注解，手工参照greet2修改commands.ts文件
#[tauri::command]
pub async fn get_security(args: GetParams) -> IpcResponse<Security> {
    println!("(commands::get_security) args={:?}", args);

    let rs: Result<Security> = fudata::db::security::find(&args.id).await;

    if let Ok(v) = &rs {
        println!("(commands::get_security) found  {:?}", v);

        let hi = fudata::model::history::FudaHistory {
            id: None,
            entity_type: Some(fudata::model::history::EntityType::Security),
            entity_id: Some(args.id.to_string()),
            entity_name: v.name.clone(),
            ..Default::default()
        };

        let _rs = history::upsert(&hi).await;
        println!("(commands::get_security) history::upsert rs= {:?}", _rs);
    }

    rs.into()
}

// 由于Security没用specta::specta注解，手工参照greet2修改commands.ts文件
#[tauri::command]
pub async fn list_kline_flats(args: ListParams<KlineFilter>) -> IpcResponse<Vec<KlineFlat>> {
    println!("\n(commands:list_kline_flats) args={:?}", args);

    let (filter, page, size) = match (&args.filter, args.page, args.size) {
        (Some(filter), Some(page), Some(size)) => (filter, page, size),
        _ => {
            return Err(anyhow::anyhow!("(commands::list_kline_flats) Either filter:{:?}, page:{:?}, or size:{:?} is miss, args.filtering.", args.filter, args.page, args.size)).into();
        }
    };

    let security_id = match &filter.security_id {
        Some(id) => id,
        None => {
            return Err(anyhow::anyhow!(
                "(commands::list_kline_flats) Id is missing in the SecurityFilter."
            ))
            .into()
        }
    };

    let klt = match filter.klt {
        Some(klt) => klt,
        None => {
            return Err(anyhow::anyhow!(
                "(commands::list_kline_flats) Klt is missing in the SecurityFilter."
            ))
            .into()
        }
    };

    let rs = fudata::db::kline::select_kline_flats(&security_id, &klt, page, size).await;

    // println!("(commands:list_kline_flats) rs= {:?} (/commands:list_kline_flats)", rs);

    match rs {
        Ok(v) => Ok(v).into(),
        Err(e) => Err(e).into(),
    }
}

// 由于Security没用specta::specta注解，手工参照greet2修改commands.ts文件
#[tauri::command]
pub async fn list_kline_datapoints(
    args: ListParams<KlineFilter>,
) -> IpcResponse<ChartJsDataWrapper> {
    println!("\n(commands:list_kline_datapoints) args={:?}", args);

    let (filter, page, size) = match (&args.filter, args.page, args.size) {
        (Some(filter), Some(page), Some(size)) => (filter, page, size),
        _ => {
            return Err(anyhow::anyhow!("(commands::list_kline_datapoints) Either filter:{:?}, page:{:?}, or size:{:?} is miss, args.filtering.", args.filter, args.page, args.size)).into();
        }
    };

    let security_id = match &filter.security_id {
        Some(id) => id,
        None => {
            return Err(anyhow::anyhow!(
                "(commands::list_kline_datapoints) Id is missing in the SecurityFilter."
            ))
            .into()
        }
    };

    let klt = match filter.klt {
        Some(klt) => klt,
        None => {
            return Err(anyhow::anyhow!(
                "(commands::list_kline_datapoints) Klt is missing in the SecurityFilter."
            ))
            .into()
        }
    };

    let rs = fudata::db::kline::select_kline_flats(&security_id, &klt, page, size).await;

    // println!("(commands:list_kline_datapoints) rs= {:?} (/commands:list_kline_datapoints)", rs);

    match rs {
        Ok(kls) => {
            let dps = kls
                .into_iter()
                .map(|k| FinancialDataPoint::from(k))
                .collect::<Vec<FinancialDataPoint>>();

            Ok(ChartJsDataWrapper {
                klines: Some(dps),
                ..Default::default()
            })
            .into()
        }
        Err(e) => Err(e).into(),
    }
}

/// 由于Security用不了specta::specta注解，手工参照greet2修改commands.ts文件
#[tauri::command]
pub async fn list_trend_line_klines(args: ListParams<KlineFilter>) -> IpcResponse<TrendLineResult> {
    println!("\n(commands:list_trend_line_klines) args={:?}", args);

    let (filter, page, size) = match (&args.filter, args.page, args.size) {
        (Some(filter), Some(page), Some(size)) => (filter, page, size),
        _ => {
            return Err(anyhow::anyhow!("(commands::list_trend_line_klines) Either filter:{:?}, page:{:?}, or size:{:?} is miss, args.filtering.", args.filter, args.page, args.size)).into();
        }
    };

    let security_id = match &filter.security_id {
        Some(id) => id,
        None => {
            return Err(anyhow::anyhow!(
                "(commands::list_trend_line_klines) security_id is missing in the SecurityFilter."
            ))
            .into()
        }
    };

    let klt = match filter.klt {
        Some(klt) => klt,
        None => {
            return Err(anyhow::anyhow!(
                "(commands::list_trend_line_klines) Klt is missing in the SecurityFilter."
            ))
            .into()
        }
    };

    let rs = fudata::db::kline::get_trend_line_klines(&security_id, &klt, page, size).await;

    println!("(commands:get_trend_line_klines) rs = {:?}", rs);

    match rs {
        Ok(v) => Ok(v).into(),
        Err(e) => Err(e).into(),
    }
}
#[tauri::command]
pub async fn list_indicators_correlation_matrix(
    args: ListParams<KlineFilter>,
) -> IpcResponse<CorrelationMatrix> {
    println!(
        "\n(commands:list_indicators_correlation_matrix) args={:?}",
        args
    );

    let (filter, page, size) = match (&args.filter, args.page, args.size) {
        (Some(filter), Some(page), Some(size)) => (filter, page, size),
        _ => {
            return Err(anyhow::anyhow!("(commands::list_indicators_correlation_matrix) Either filter:{:?}, page:{:?}, or size:{:?} is miss, args.filtering.", args.filter, args.page, args.size)).into();
        }
    };

    let security_id = match &filter.security_id {
        Some(id) => id,
        None => {
            return Err(anyhow::anyhow!(
            "(commands::list_indicators_correlation_matrix) Id is missing in the SecurityFilter."
        ))
            .into()
        }
    };

    let klt = match filter.klt {
        Some(klt) => klt,
        None => {
            return Err(anyhow::anyhow!(
            "(commands::list_indicators_correlation_matrix) Klt is missing in the SecurityFilter."
        ))
            .into()
        }
    };

    let rs = fudata::db::kline::select_correlation_matrix(&security_id, &klt, page, size).await;

    match rs {
        Ok(v) => Ok(v).into(),
        Err(e) => Err(e).into(),
    }
}

#[tauri::command]
pub async fn list_indicator_combinations(
    args: ListParams<IndicatorCombinationFilter>,
) -> IpcResponse<Vec<IndicatorComb>> {
    println!(
        "\n(commands:list_indicators_correlation_matrix) args={:?}",
        args
    );

    let (
        IndicatorCombinationFilter {
            security_id,
            klt,
            comb_num,
        },
        size,
    ) = match (args.filter, args.size) {
        (Some(filter), Some(size)) => (filter, size),
        _ => {
            return Err(anyhow::anyhow!("Either filter, or size is missing.")).into();
        }
    };

    let rs =
        fudata::db::kline::get_indicator_combinations(&security_id, &klt, comb_num, size).await;
    match rs {
        Ok(v) => Ok(v).into(),
        Err(e) => Err(e).into(),
    }
}
// 由于Security没用specta::specta注解，手工参照greet2修改commands.ts文件
#[tauri::command]
pub async fn get_kline_flat_table() -> IpcResponse<Table> {
    println!("\n(commands:get_kline_flat_table) ");
    let rs: Result<Table, anyhow::Error> = fudata::db::table::get_kline_flat_table().await;
    // println!("(get_kline_flat_table) rs: {:?}", rs);

    rs.into()
}

#[tauri::command]
pub async fn update_kline_flat_table_column(args: UpdateParams<Column>) -> IpcResponse<Column> {
    println!("\n(commands:update_kline_flat_table_column) ");
    let rs: Result<Column, anyhow::Error> =
        fudata::db::table::update_kline_flat_table_column(args.data).await;
    println!("(update_kline_flat_table_column) rs: {:?}", rs);

    rs.into()
}

#[test]
fn export_bindings() {
    tauri_specta::ts::export(
        specta::collect_types![greet, greet2],
        "./sample_commands.ts",
    )
    .unwrap();
    // js::export(collect_types![greet, greet2, greet3], "../src/bindings.js").unwrap();
}

/// 由于TradeStrategy用不了specta::specta注解，手工参照greet2修改commands.ts文件
#[tauri::command]
pub async fn list_trade_strategies(
    args: ListParams<TradeStrategyFilter>,
) -> IpcResponse<Vec<TradeStrategy>> {
    println!("(commands::list_trade_strategies) args={:?}", args);
    let mut pi: u16 = 0;
    let mut pn: u16 = 10;
    if let Some(page) = args.page {
        pi = page;
    }
    if let Some(size) = args.size {
        pn = size;
    }

    let klt = if let Some(filter) = args.filter {
        filter.klt
    } else {
        None
    };

    let rs: Result<Vec<TradeStrategy>> = fudata::db::trade_strategy::select(klt, pi, pn).await;

    rs.into()
}

/// 由于TradeStrategy用不了specta::specta注解，手工参照greet2修改commands.ts文件
#[tauri::command]
pub async fn get_trade_strategy(args: GetParams) -> IpcResponse<TradeStrategy> {
    println!("(commands::get_trade_strategy) args={:?}", args);

    let rs: Result<TradeStrategy> = fudata::db::trade_strategy::find_trade_strategy(&args.id).await;

    rs.into()
}

/// 由于TradeStrategy用不了specta::specta注解，手工参照greet2修改commands.ts文件
#[tauri::command]
pub async fn verify_trade_strategy(args: GetParams) -> IpcResponse<String> {
    println!("(commands::verify_trade_strategy) args={:?}", args);

    let tstg: Result<TradeStrategy> =
        fudata::db::trade_strategy::find_trade_strategy(&args.id).await;
    match tstg {
        Ok(mut tstg) => {
            let mut trade_engine = TradeEngine::new("trade_bot_id001", 3);
            let rs: Result<AST> =
                trade_engine.verify_script(&tstg.script.clone().unwrap_or_default());

            match rs {
                Ok(_ast) => {
                    tstg.script_verified = Some(true);
                    let _ =
                        fudata::db::trade_strategy::update_trade_strategy(&args.id, &tstg).await;
                    Ok("Passed".to_string()).into()
                }
                Err(e) => Err(anyhow::anyhow!(e)).into(),
            }
        }
        Err(e) => Err(e).into(),
    }
}

/// 由于TradeStrategy用不了specta::specta注解，手工参照greet2修改commands.ts文件
#[tauri::command]
pub async fn update_trade_strategy(
    args: UpdateParams<TradeStrategy>,
) -> IpcResponse<TradeStrategy> {
    println!("(commands::update_trade_strategy) args={:?}", args);

    let rs: Result<TradeStrategy> =
        fudata::db::trade_strategy::update_trade_strategy(&args.id, &args.data).await;
    rs.into()
}

/// 由于TradeStrategy用不了specta::specta注解，手工参照greet2修改commands.ts文件
#[tauri::command]
pub async fn update_trade_strategy_indicator(
    args: UpdateParams<TradeStrategyIndicator>,
) -> IpcResponse<TradeStrategy> {
    println!(
        "(commands::update_trade_strategy_indicator) args={:?}",
        args
    );

    let rs: Result<TradeStrategy> =
        fudata::db::trade_strategy::update_trade_strategy_indicator(&args.id, &args.data).await;
    rs.into()
}

/// 由于TradeStrategy用不了specta::specta注解，手工参照greet2修改commands.ts文件
#[tauri::command]
pub async fn update_trade_strategy_maline(
    args: UpdateParams<TradeStrategyMaLine>,
) -> IpcResponse<TradeStrategy> {
    println!("(commands::update_trade_strategy_maline) args={:?}", args);

    let rs: Result<TradeStrategy> =
        fudata::db::trade_strategy::update_trade_strategy_maline(&args.id, &args.data).await;
    rs.into()
}

/// 由于TradeStrategy用不了specta::specta注解，手工参照greet2修改commands.ts文件
#[tauri::command]
pub async fn publish_trade_strategy(args: GetParams) -> IpcResponse<Option<FudaRecord>> {
    println!("(commands::publish_trade_strategy) args={:?}", args);

    let rs: Result<Option<FudaRecord>> =
        fudata::db::trade_strategy::publish_trade_strategy(&args.id).await;

    println!("(commands::publish_trade_strategy) rs={:?}", rs);
    rs.into()
}

/// 由于 TradeBot 用不了specta::specta注解，手工参照greet2修改commands.ts文件
#[tauri::command]
pub async fn list_trade_bots(args: ListParams<TradeBotFilter>) -> IpcResponse<Vec<TradeBot>> {
    println!("(commands::list_trade_bots) args={:?}", args);
    let mut pi: u16 = 0;
    let mut pn: u16 = 10;
    if let Some(page) = args.page {
        pi = page;
    }
    if let Some(size) = args.size {
        pn = size;
    }

    let (security_id, strategy_id) = if let Some(filter) = args.filter {
        (filter.security_id, filter.strategy_id)
    } else {
        (None, None)
    };

    let rs: Result<Vec<TradeBot>> =
        fudata::db::trade_bot_with::select(&security_id, &strategy_id, pi, pn).await;

    // println!("(commands::list_trade_bots) rs={:?}", rs);

    rs.into()
}

/// 由于TradeStrategy用不了specta::specta注解，手工参照greet2修改commands.ts文件
#[tauri::command]
pub async fn update_trade_bot(args: UpdateParams<TradeBot>) -> IpcResponse<Option<FudaRecord>> {
    println!("(commands::update_trade_bot) args={:?}", args);

    let rs: Result<Option<FudaRecord>> =
        fudata::db::trade_bot_with::update(&args.id, &args.data).await;
    rs.into()
}

/// 由于 TradeBot 用不了specta::specta注解，手工参照greet2修改commands.ts文件
#[tauri::command]
pub async fn list_indicators(args: ListParams<IndicatorFilter>) -> IpcResponse<Vec<Indicator>> {
    println!("(commands::list_indicators) args={:?}", args);
    let mut pi: u16 = 0;
    let mut pn: u16 = 10;
    if let Some(page) = args.page {
        pi = page;
    }
    if let Some(size) = args.size {
        pn = size;
    }

    let rs: Result<Vec<Indicator>> = fudata::db::indicators::select(pi, pn).await;

    // println!("(commands::list_indicators) rs={:?}", rs);

    rs.into()
}

/// 由于 TradeBot 用不了specta::specta注解，手工参照greet2修改commands.ts文件
#[tauri::command]
pub async fn list_trade_orders(args: ListParams<TradeOrderFilter>) -> IpcResponse<Vec<TradeOrder>> {
    println!("(commands::list_trade_orders) args={:?}", args);
    let mut pi: u16 = 0;
    let mut pn: u16 = 10;
    if let Some(page) = args.page {
        pi = page;
    }
    if let Some(size) = args.size {
        pn = size;
    }

    if let Some(TradeOrderFilter {
        trade_bot_id,
        start_kline_id,
        end_kline_id,
        ..
    }) = args.filter
    {
        let rs: Result<Vec<TradeOrder>> =
            fudata::db::trade_order::select(trade_bot_id, start_kline_id, end_kline_id, pi, pn)
                .await;
        return rs.into();
    } else {
        let rs: Result<Vec<TradeOrder>> =
            fudata::db::trade_order::select(None, None, None, pi, pn).await;
        return rs.into();
    };

    // println!("(commands::list_trade_orders) rs={:?}", rs);

    // rs.into()
}

/// 由于TradeBot用不了specta::specta注解，手工参照greet2修改commands.ts文件
#[tauri::command]
pub async fn get_trade_bot(args: GetParams) -> IpcResponse<TradeBot> {
    println!("(commands::get_trade_bot) args={:?}", args);

    let rs: Result<TradeBot> = fudata::db::trade_bot_with::find(&args.id).await;

    if let Ok(tb) = &rs {
        let hi = fudata::model::history::FudaHistory {
            id: None,
            entity_type: Some(fudata::model::history::EntityType::Tbot),
            entity_id: Some(args.id.to_string()),
            entity_name: tb.security_name.clone(),
            ..Default::default()
        };

        let _rs = history::upsert(&hi).await;
        println!("(commands::get_trade_bot) history::upsert rs= {:?}", _rs);
    }

    rs.into()
}

#[tauri::command]
pub async fn start_trade_bot_backtest(args: GetParams) -> IpcResponse<Vec<TradeRequest>> {
    println!("(commands::start_trade_bot_backtest) args={:?}", args);

    let res = db::trade_bot_with::find(&args.id).await;
    match res {
        Ok(tb) => {
            let mut trade_engine: TradeEngine = TradeEngine::new(
                &args.id, //"j9tpv2dyh3op2jwgr107",
                match tb.kl_window {
                    Some(v) => v,
                    None => 1,
                },
            );
            let rs = back_test::start_trade_bot_backtest(&mut trade_engine).await;
            println!("(commands::start_trade_bot_backtest) rs={:?}", rs);

            rs.into()
        }
        Err(e) => {
            let error_result: Result<Vec<TradeRequest>, anyhow::Error> = Err(e);
            error_result.into()
        }
    }
}

/// 由于TradeStrategy用不了specta::specta注解，手工参照greet2修改commands.ts文件
#[tauri::command]
pub async fn list_history(args: ListParams<FudaHistoryFilter>) -> IpcResponse<Vec<FudaHistory>> {
    println!("(commands::list_history) args={:?}", args);
    let mut pi: u16 = 0;
    let mut pn: u16 = 10;
    if let Some(page) = args.page {
        pi = page;
    }
    if let Some(size) = args.size {
        pn = size;
    }

    let rs: Result<Vec<FudaHistory>> = fudata::db::history::select(None, None, pi, pn).await;

    rs.into()
}
